查看原文
其他

C# 9 record 并非简单属性 POCO 的语法糖

DotNet 2021-09-23

(给DotNet加星标,提升.Net技能

转自:暐翰
cnblogs.com/ITWeiHan/p/14028756.html

最近升级专案到大统一 .NET 5 并使用 C#9 语法尝试改写套件,发现之前以为 record 只是简单属性 POCO 的简化语法糖的认知是错误。


另外因为 POCO 属于需定义口语词,这边在本文定义简单属性 POCO 为 public class 类别 {public string ID{get;set}/*略*/} 只有属性的简单类别代码


一、rocord 的确底层是 class,但,不是单纯简单属性 POCO class


可以看 IL Spy 反编译程序码,发现系统帮我们做了很多事



二、预设生成的是属性是 {get;init;} 不是 {get;set;},这代表设定值时间点在 constructor(建构式),延伸产生immutable(不可变)特性,也代表 record 预设为thread-safe(线程安全),因为都是取得一样的值。


所以当你使用 Dapper 类似框架查询完 POCO 资料,想做修改属性时会报 CS8852 无法修改错误。



三、预设比较逻辑改变


可以看TimCorey写的例子,可以看到预设 class 跟 record 的 == 差异,线上测试连结


public class Program
{
public static void Main()
{
var record1Obj1 = new record1(FirstName: "Lin", LastName: "WeiHan");
var record1Obj2 = new record1(FirstName: "Lin", LastName: "WeiHan");
Console.WriteLine(record1Obj1 == record1Obj2);//true
var class1Obj1 = new Class1() { FirstName = "Lin", LastName = "WeiHan" };
var class2Obj2 = new Class1() { FirstName = "Lin", LastName = "WeiHan" };
Console.WriteLine(class1Obj1 == class2Obj2);//false
}
}

public record record1(string FirstName,string LastName);

public class Class1
{
public string FirstName {get;init;}
public string LastName{get;init;}
}



因为 record override == 跟 Equals,认为只要是同一个 record 类型,并且属性值都一样,系统就会认定为true,也就是俗称的structural equality,可以看 IL Spy 反编译代码


public virtual bool Equals(record2? other)
{
return (object)other != null && EqualityContract ==
other!.EqualityContract &&
EqualityComparer<string>.Default.Equals(FirstName,
other!.FirstName) &&
EqualityComparer<string>.Default.Equals(LastName,
other!.LastName);
}


跟 object class 预设会去取得 RuntimeHelpers.GetHashCode Handle 逻辑不相同。


四、GetHashCode也做了类似逻辑,所以属性值一样,HashCode会得到一样的值,线上测试连结



IL Spy 反编译代码


public override int GetHashCode()
{
return
(EqualityComparer<Type>.Default.GetHashCode(EqualityContract)
* -1521134295 +
EqualityComparer<string>.Default.GetHashCode(FirstName)) *
-1521134295 +
EqualityComparer<string>.Default.GetHashCode(LastName);
}


五、注意不能把 record 当作一定是 immutable(不可变),原因在微软没有限制以下写法...


public record record2
{
public string FirstName {get;set;}
public string LastName{get;set;}
}



准许修改 {get;init;} 为 {get;set},将会导致 immutable 跟 thread-safe 特性消失



六、record 会帮忙生成可读性好的 ToString 实作

以下图片为比较一般 class 跟 record 生成的 ToString 差别


七、record 帮忙生成 extend IEquatable<类别> ,并实作强型别public virtual bool Equals(Record1? other)



这代表可以避免原本public override bool Equals(object? obj)需要先 unboxing 再 boxing 的效能损耗问题



- EOF -


推荐阅读  点击标题可跳转
.NET Core消息流处理流程.NET Core 的热插拔机制的深入探索.NET 开源项目 FreeRedis 实现思路


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

点赞和在看就是最大的支持❤️

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存